{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Programming a Chess Player" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "CS371: Introduction to Cognitive Science \n", "Bryn Mawr College \n", "Department of Computer Science \n", "Professor Blank, Fall 2016" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Goals**:\n", "\n", "* explore the idea that a computer could \"think\"\n", "* explore symbolic computation\n", "* write a program to play Chess\n", "\n", "In this notebook we will begin to explore *symbolic computation* by writing a program to play Chess. Perhaps you don't know how to play Chess... no problem! You don't really need to know much, but a primer on Chess may be useful. Here are some links that might be useful:\n", "\n", "**Getting Started with Chess**:\n", "\n", "* http://www.chesscorner.com/tutorial/learn.htm\n", "* https://www.chesscademy.com/\n", "* http://learningchess.net/us/index\n", "* https://www.chess.com/learn-how-to-play-chess\n", "\n", "For these experiments, we will use the `python-chess` library. In this notebook, we will define three different sample players. We explore them in some depth here to attempt to understand how each plays chess.\n", "\n", "**python-chess Reference**:\n", "\n", "* https://python-chess.readthedocs.io/en/v0.15.0/core.html" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.1 Game Play" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first thing we need to do is import the chess library:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import chess" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will use the chess library in the following manner:\n", "\n", "1. Create a chess.Board instance\n", "1. The chess.Board instance automatically generates all possible moves for the current player\n", "1. Current player picks a move\n", "1. Go to step #2 and repeat until win, lose, or draw\n", "\n", "That's it! Thus we have reduced the playing a valid game of chess into simply selecting a move at each turn. To play a good game of chess, you will want to pick \"the best move\" at each turn. \n", "\n", "A player will be a function that takes a board instance as a argument, and returns a move encoded as a string in Universal Chess Interface format:\n", "\n", "```python\n", "def player(board):\n", " ### \"Thinking\" happens here\n", " return move_code\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll explain this fully below." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.2 The Board class" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Board class keeps track of whose turn it is, possible moves, and a history of all past moves. This class can undo and redo moves, and keeps track of repeating states. \n", "\n", "First, we create a board instance:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "board = chess.Board()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `board.turn` value is a boolean indicating whose turn it is. The values are either True for white or False for black." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "board.turn" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As seen above, the game always begins with white's turn. If you forget which is True, you can ask the chess module:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "board.turn == chess.WHITE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The chess.Board class is responsible for generating all possible moves, whose turn it is, keeping track of the placement of all pieces, and making each move. The chess.Board represents a two-dimensional 8 x 8 array. However, the internal representation is optimized for speedy operations.\n", "\n", "Here is a visual representation of a chess.Board:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/svg+xml": [ "aabbccddeeffgghh1122334455667788" ], "text/plain": [ "Board('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1')" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "board" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also get an ASCII board representation (text-only) by converting the board into a string:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "r n b q k b n r\n", "p p p p p p p p\n", ". . . . . . . .\n", ". . . . . . . .\n", ". . . . . . . .\n", ". . . . . . . .\n", "P P P P P P P P\n", "R N B Q K B N R\n" ] } ], "source": [ "print(str(board))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The string representation of the board shows the character representation for each piece. Specifically:\n", "\n", "Piece | White | Black\n", "------|-------|--------\n", "Pawn | P | p\n", "Rook | R | r\n", "Knight | N | n\n", "Bishop | B | b\n", "Queen | Q | q\n", "King | K | k\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For our uses, you don't really need to know how each piece moves. We discuss game strategy, though, shortly.\n", "\n", "The 2-dimensional board is laid out so that each position is indicated by a column letter and row number. However, the internal representation is sequential. Say that we wanted to see what was at location 'c1' we could use:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "chess.C1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "to get the internal location of the column/row, and then use:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "Piece.from_symbol('B')" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "board.piece_at(chess.C1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or shown as a character:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'B'" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "str(board.piece_at(chess.C1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Indeed, there is a white bishop at 'c1'." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.3 Making Moves" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "At this point, we can as the board instance to generate all of the possible, legal moves:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[Move.from_uci('b1a3'),\n", " Move.from_uci('b1c3'),\n", " Move.from_uci('g1f3'),\n", " Move.from_uci('g1h3'),\n", " Move.from_uci('a2a3'),\n", " Move.from_uci('b2b3'),\n", " Move.from_uci('c2c3'),\n", " Move.from_uci('d2d3'),\n", " Move.from_uci('e2e3'),\n", " Move.from_uci('f2f3'),\n", " Move.from_uci('g2g3'),\n", " Move.from_uci('h2h3'),\n", " Move.from_uci('a2a4'),\n", " Move.from_uci('b2b4'),\n", " Move.from_uci('c2c4'),\n", " Move.from_uci('d2d4'),\n", " Move.from_uci('e2e4'),\n", " Move.from_uci('f2f4'),\n", " Move.from_uci('g2g4'),\n", " Move.from_uci('h2h4')]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(board.legal_moves)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Python Note**: `board.legal_moves` looks like a normal list of items. But it is really a property that gets lazily generated on the fly. We force it to be a list by wrapping `list()` around it.\n", "\n", "We can get the first move (index zero):" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [], "source": [ "move = list(board.legal_moves)[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1.3.1 Universal Chess Interface" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Universal Chess Interface (or uci) is a representation for describing a move from one cell to another (and perhaps additional information as well). We explore the first move:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Move.from_uci('b1a3')" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "move" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'b1a3'" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "move.uci()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Thus, this is a move from b1 to a3. \n", "\n", "What piece is this, and where is it moving on the board? Is this a good move?\n", "\n", "The uci string is what each player function will return." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1.3.2 Standard Algebraic Notation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you know something about Chess, you might know about Standard Algebraic Notation (or san). This is an alternative to uci. You can get a move's san with:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'Na3'" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "board.san(move)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, we will always use uci." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.4 Programming a Random Player" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is a useful function in the random module that will select from a a list of choices. This is called `random.choice`." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import random" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To use it in a function, we simply:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def random_player(board):\n", " move = random.choice(list(board.legal_moves))\n", " return move.uci()" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'h2h4'" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "random_player(board)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "h2h3\n", "a2a4\n", "g1h3\n", "b2b3\n", "b2b3\n", "e2e4\n", "g1f3\n", "d2d4\n", "c2c4\n", "g1f3\n" ] } ], "source": [ "for i in range(10):\n", " print(random_player(board))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.5 Playing a Game" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To play a game, we'll write a new function called `play_game` that will take two player functions, create a board, and alternatively call the player functions until a win, lose, or draw.\n", "\n", "First, we need some additional modules for displaying a game in the notebook:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import time\n", "from IPython.display import display, HTML, clear_output" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A useful function for displaying the color of a player:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def who(player):\n", " return \"White\" if player == chess.WHITE else \"Black\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A function for displaying the board as text, or as the nice image (called SVG):" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def display_board(board, use_svg):\n", " if use_svg:\n", " return board._repr_svg_()\n", " else:\n", " return \"
\" + str(board) + \"
\"\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And finally, we can put those together to play a game:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def play_game(player1, player2, visual=\"svg\", pause=0.1):\n", " \"\"\"\n", " playerN1, player2: functions that takes board, return uci move\n", " visual: \"simple\" | \"svg\" | None\n", " \"\"\"\n", " use_svg = (visual == \"svg\")\n", " board = chess.Board()\n", " try:\n", " while not board.is_game_over(claim_draw=True):\n", " if board.turn == chess.WHITE:\n", " uci = player1(board)\n", " else:\n", " uci = player2(board)\n", " name = who(board.turn)\n", " board.push_uci(uci)\n", " board_stop = display_board(board, use_svg)\n", " html = \"Move %s %s, Play '%s':
%s\" % (\n", " len(board.move_stack), name, uci, board_stop)\n", " if visual is not None:\n", " if visual == \"svg\":\n", " clear_output(wait=True)\n", " display(HTML(html))\n", " if visual == \"svg\":\n", " time.sleep(pause)\n", " except KeyboardInterrupt:\n", " msg = \"Game interrupted!\"\n", " return (None, msg, board)\n", " result = None\n", " if board.is_checkmate():\n", " msg = \"checkmate: \" + who(not board.turn) + \" wins!\"\n", " result = not board.turn\n", " elif board.is_stalemate():\n", " msg = \"draw: stalemate\"\n", " elif board.is_fivefold_repetition():\n", " msg = \"draw: 5-fold repetition\"\n", " elif board.is_insufficient_material():\n", " msg = \"draw: insufficient material\"\n", " elif board.can_claim_draw():\n", " msg = \"draw: claim\"\n", " if visual is not None:\n", " print(msg)\n", " return (result, msg, board)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function takes to player functions (first white, then black), and an optional argument to indicate representation style.\n", "\n", "Let's pit random_player vs. random_player:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "Move 282 Black, Play 'g8h8':
aabbccddeeffgghh1122334455667788" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "draw: claim\n" ] }, { "data": { "text/plain": [ "(None, 'draw: claim', Board('7k/8/8/1K6/8/8/2R5/8 w - - 21 142'))" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "play_game(random_player, random_player)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Many times, that will end in a draw." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.6 Allowing a Human Player" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Do you want to play a game? Here is a way to play:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def human_player(board):\n", " display(board)\n", " uci = get_move(\"%s's move [q to quit]> \" % who(board.turn))\n", " legal_uci_moves = [move.uci() for move in board.legal_moves]\n", " while uci not in legal_uci_moves:\n", " print(\"Legal moves: \" + (\",\".join(sorted(legal_uci_moves))))\n", " uci = get_move(\"%s's move[q to quit]> \" % who(board.turn))\n", " return uci" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And a helper function to handle the input:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def get_move(prompt):\n", " uci = input(prompt)\n", " if uci and uci[0] == \"q\":\n", " raise KeyboardInterrupt()\n", " try:\n", " chess.Move.from_uci(uci)\n", " except:\n", " uci = None\n", " return uci" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that you must enter your move in UCI, such as \"a2a4\", meaning moving the piece at a2 to location a4.\n", "\n", "Try you hand at playing chess against the random_player. It is not as easy as it sounds. Did you win? How many turns did it take?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.7 Analysis" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If a random_player plays a random_player many times, how many times would you expect white to win? Black to win? To end in a draw?\n", "\n", "Let's try it:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [], "source": [ "counts = {None: 0, True: 0, False: 0}\n", "for i in range(10):\n", " result, msg, board = play_game(random_player, random_player, visual=None)\n", " counts[result] += 1\n", " print(counts)\n", "counts" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.8 Static Analysis/Board Evaluation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The next sample player takes each possible move, applies it to a temporary board and state, and then goes through the board, place by place, in order to compute an evaluation score for each resulting state. The moves are sorted by this score, and the best move is then returned:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def player1(board):\n", " moves = list(board.legal_moves)\n", " for move in moves:\n", " newboard = board.copy()\n", " # go through board and return a score\n", " move.score = staticAnalysis(newboard, move, board.turn)\n", " moves.sort(key=lambda move: move.score, reverse=True) # sort on score\n", " return moves[0].uci()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The actual score is computed by the staticAnalysis function which is designed to evaluate the resulting board after each hypothesized move. To come up with a score for each static snapshot of a board, it will be necessary to know how many of each piece is left, and where they are. You can use the `board.pieces()` method for this:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/svg+xml": [ "aabbccddeeffgghh1122334455667788" ], "text/plain": [ "SquareSet(0b10000001)" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "board = chess.Board()\n", "\n", "board.pieces(chess.ROOK, True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you look at the output as a list, you'll see the 1-D representation of where those pieces are on the game board:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[0, 7]" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(board.pieces(chess.ROOK, True))" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(board.pieces(chess.ROOK, True))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are 2 white rooks.\n", "\n", "Now, putting that into a function, checking for each type of piece:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def staticAnalysis(board, move, my_color):\n", " score = 0\n", " ## Check some things about this move:\n", " # score += 10 if board.is_capture(move) else 0\n", " # To actually make the move:\n", " board.push(move)\n", " # Now check some other things:\n", " for (piece, value) in [(chess.PAWN, 1), \n", " (chess.BISHOP, 4), \n", " (chess.KING, 0), \n", " (chess.QUEEN, 10), \n", " (chess.KNIGHT, 5),\n", " (chess.ROOK, 3)]:\n", " score += len(board.pieces(piece, my_color)) * value\n", " score -= len(board.pieces(piece, not my_color)) * value\n", " # can also check things about the pieces position here\n", " return score" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "play_game(player1, random_player)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**NOTE**: The string representation for the board is in [Forsyth-Edwards Notation](https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation), or FEN for short. The last number (6th column) is the \"full-move count\". If the full-move count is 36, then there have been 35 * 2 full-moves, plus 1 if \"b\" is in second columns, for 71 moves.\n", "\n", "That didn't play so well! Why not?\n", "\n", "The following is one way around the problem. What does it do differently?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def staticAnalysis(board, move, my_color):\n", " score = random.random()\n", " ## Check some things about this move:\n", " # score += 10 if board.is_capture(move) else 0\n", " # To actually make the move:\n", " board.push(move)\n", " # Now check some other things:\n", " for (piece, value) in [(chess.PAWN, 1), \n", " (chess.BISHOP, 4), \n", " (chess.KING, 0), \n", " (chess.QUEEN, 10), \n", " (chess.KNIGHT, 5),\n", " (chess.ROOK, 3)]:\n", " score += len(board.pieces(piece, my_color)) * value\n", " score -= len(board.pieces(piece, not my_color)) * value\n", " # can also check things about the pieces position here\n", " return score" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "play_game(player1, random_player)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Better! But it still is not very aggressive. What could we add to make it attack?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def staticAnalysis(board, move, my_color):\n", " score = random.random()\n", " ## Check some things about this move:\n", " # score += 10 if board.is_capture(move) else 0\n", " # To actually make the move:\n", " board.push(move)\n", " # Now check some other things:\n", " for (piece, value) in [(chess.PAWN, 1), \n", " (chess.BISHOP, 4), \n", " (chess.KING, 0), \n", " (chess.QUEEN, 10), \n", " (chess.KNIGHT, 5),\n", " (chess.ROOK, 3)]:\n", " score += len(board.pieces(piece, my_color)) * value\n", " score -= len(board.pieces(piece, not my_color)) * value\n", " # can also check things about the pieces position here\n", " # Check global things about the board\n", " score += 100 if board.is_checkmate() else 0\n", " return score" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "play_game(player1, random_player)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Not bad!\n", "\n", "This staticAnalysis function makes a much better player than either of the random players, but it still has major issues. How can you improve this static evaluation function?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.9 Suggestions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pawns get promoted when they get to the back row. Encourage them to get to the back row (eg, the closer they are to the opposite side, the better).\n", "\n", "* It is good to threaten opponent pieces.\n", "* It is good that your opponent's King has no valid moves.\n", "\n", "Static analysis on the next move's state can only do so much good. It would be better if you could \"look ahead\" further and see the results of what your opponent could do, given what your proposed move did. And then see what you could do, then what they would do, etc. This is how real chess programs work. There are many algorithms for finding the best move by looking many moves ahead, such as minimax and alpha-beta pruning. You'll explore these ideas fully in Artificial Intelligence.\n", "\n", "Your board evaluation function could change during the game. For example, you might use one evaluation function at the beginning, one in the middle, and another at the end. How can you tell where you are in a game?\n", "\n", "There is a nice article on Chess Strategy at wikipedia: http://en.wikipedia.org/wiki/Chess_strategy" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 1 }